//
// Created by zsj on 2019/12/21.
//

#ifndef SCLI_RSA_CRYPTO_H
#define SCLI_RSA_CRYPTO_H

#include "crypto_algorithm.h"
#include "openssl/rsa.h"
#include "openssl/pem.h"

class rsa_crypto : public crypto_algorithm {
public:
    virtual const string encrypt() {
        BIO *bio_key = BIO_new_mem_buf(key.c_str(), -1);
        RSA *rsa = NULL;

        if (key.find("-----BEGIN RSA") == 0) {
            // PKCS#1
            PEM_read_bio_RSAPublicKey(bio_key, &rsa, NULL, NULL);
        } else if (key.find("-----BEGIN PUBLIC") == 0) {
            // PKCS#8
            PEM_read_bio_RSA_PUBKEY(bio_key, &rsa, NULL, NULL);
        } else {
            BIO_free(bio_key);
            __err("不支持的公钥格式。（支持PKCS#1和PKCS#8）");
        }

        int len = RSA_size(rsa);
        int flen = len - RSA_PKCS1_PADDING_SIZE;
        unsigned long b = blocks(flen, plain);
        char *encrypted_text = (char *) malloc(len);
        cipher.clear();


        for (unsigned long i = 0; i < b; i++) {
            memset(encrypted_text, 0, len);
            string subs = plain.substr(i * flen, flen);
            int ret = RSA_public_encrypt(subs.length(),
                                         (const unsigned char*) subs.c_str(),
                                         (unsigned char*) encrypted_text, rsa, RSA_PKCS1_PADDING);

            if (ret >= 0) {
                cipher += std::string(encrypted_text, ret);
            } else {
                // 释放内存
                free(encrypted_text);
                BIO_free_all(bio_key);
                RSA_free(rsa);
                __err("加密出现错误。");
            }
        }

        // 释放内存
        free(encrypted_text);
        BIO_free_all(bio_key);
        RSA_free(rsa);

        return cipher;
    }

    virtual const string decrypt() {
        BIO *bio_key = BIO_new_mem_buf(key.c_str(), -1);
        RSA *rsa = RSA_new();

        if (key.find("-----BEGIN RSA") == 0) {
            // PKCS#1
            PEM_read_bio_RSAPrivateKey(bio_key, &rsa, NULL, NULL);
        } else if (key.find("-----BEGIN PRIVATE") == 0) {
            // PKCS#8
            PEM_read_bio_RSAPrivateKey(bio_key, &rsa, NULL, NULL);
        } else {
            BIO_free(bio_key);
            __err("不支持的私钥格式。（支持PKCS#1和PKCS#8）");
        }

        int len = RSA_size(rsa);
        unsigned long b = blocks(len, cipher);
        char *decrypted_text = (char *) malloc(len);

        for (unsigned long i = 0; i < b; i++) {
            memset(decrypted_text, 0, len);

            int ret = RSA_private_decrypt(len,
                                          (const unsigned char*) cipher.substr(i * len, len).c_str(),
                                          (unsigned char*) decrypted_text, rsa, RSA_PKCS1_PADDING);

            if (ret >= 0) {
                plain += std::string(decrypted_text, ret);
            } else {
                // 释放内存
                free(decrypted_text);
                BIO_free_all(bio_key);
                RSA_free(rsa);
                __err("解密数据出错。");
            }
        }

        // 释放内存
        free(decrypted_text);
        BIO_free_all(bio_key);
        RSA_free(rsa);

        return plain;
    }

    rsa_crypto() {

    }

    ~rsa_crypto() {}

private:
    unsigned long blocks(int flen, string data) {
        unsigned long tmp = data.length() / flen;
        if (data.length() - tmp * flen == 0) {
            return tmp;
        } else {
            return tmp + 1;
        }
    }
};

#endif //SCLI_RSA_CRYPTO_H
